﻿using System.Collections.Generic;
using System.Linq;
using Firestarter.DB.Schema.BO;
using Firestarter.DM_DB.Logic;
using FireStarterModeller.BO;
using FireStarterModeller.Test;
using Habanero.Base;
using NUnit.Framework;

namespace Firestarter.DB_DM.Logic.Test
{
    [TestFixture]
    public class TestDBFactory_CreateDBTable : TestBase
    {
        [Test]
        public void Test_CreateDBTable_TableName()
        {
            //---------------Set up test pack-------------------
            IClassDef classDef = TestUtilsDMClass.CreateUnsavedValidDMClass();
            DBFactory factory = TestDBFactory.CreateDBFactory();
            //---------------Execute Test ----------------------
            DBTable table = factory.CreateDBTable(classDef);
            //---------------Test Result -----------------------
            Assert.AreEqual(table.TableName, classDef.TableName);
        }

        [Test]
        public void Test_CreateDBTable_PropDefs()
        {
            //---------------Set up test pack-------------------
            DMClass @class = TestUtilsDMClass.CreateUnsavedValidDMClass();
            TestUtilsDMProperty.CreateUnsavedValidDMProperties(@class, 3);
            DBFactory factory = TestDBFactory.CreateDBFactory();
            //---------------Execute Test ----------------------
            DBTable table = factory.CreateDBTable(@class);
            //---------------Test Result -----------------------
            Assert.AreEqual(3, table.Columns.Count);
        }

        [Test]
        public void Test_CreateDBTable_WhenPrimaryKeyNull()
        {
            //---------------Set up test pack-------------------
            DMClass @class = TestUtilsDMClass.CreateUnsavedValidDMClass();
            TestUtilsDMProperty.CreateUnsavedValidDMProperties(@class, 3);
            DBFactory factory = TestDBFactory.CreateDBFactory();
            //---------------Assert Precondition----------------
            Assert.IsNull(@class.ObjectIdentity);
            //---------------Execute Test ----------------------
            DBTable table = factory.CreateDBTable(@class);
            //---------------Test Result -----------------------
            Assert.IsNull(table.PrimaryKeyDB);
            
        }

        [Test]
        public void Test_CreateDBTable_WhenPrimaryKeyNotNull()
        {
            //---------------Set up test pack-------------------
            DMClass @class = TestUtilsDMClass.CreateUnsavedValidDMClass(); 
            DMProperty dmProperty = TestUtilsShared.CreateUnsavedValidDMProperty(@class);
            @class.CreateObjectIdentity(dmProperty);
            DBFactory factory = TestDBFactory.CreateDBFactory();
            //---------------Assert Precondition----------------
            Assert.IsNotNull(@class.ObjectIdentity);
            //---------------Execute Test ----------------------
            DBTable table = factory.CreateDBTable(@class);
            //---------------Test Result -----------------------
            Assert.IsNotNull(table.PrimaryKeyDB);
        }

        [Test]
        public void Test_CreateDBTable_PrimaryKey_When3PropertiesAndPrimary()
        {
            //---------------Set up test pack-------------------
            DMClass @class = TestUtilsDMClass.CreateUnsavedValidDMClass(); 
            DMProperty dmProperty = TestUtilsShared.CreateUnsavedValidDMProperty(@class);
            @class.CreateObjectIdentity(dmProperty);
            @class.Properties.CreateBusinessObject();
            @class.Properties.CreateBusinessObject();
            DBFactory factory = TestDBFactory.CreateDBFactory();
            //---------------Assert Precondition----------------
            Assert.IsNotNull(@class.ObjectIdentity);
            //---------------Execute Test ----------------------
            DBTable table = factory.CreateDBTable(@class);
            //---------------Test Result -----------------------
            Assert.IsNotNull(table.PrimaryKeyDB);
            Assert.AreEqual(3, @class.Properties.Count);
        }
        
        // unique keys

        [Test]
        public void Test_CreateDBTable_WhenUniqueKeyNull()
        {
            //---------------Set up test pack-------------------
            DMClass @class = TestUtilsDMClass.CreateUnsavedValidDMClass();
            TestUtilsDMProperty.CreateUnsavedValidDMProperties(@class, 3);
            DBFactory factory = TestDBFactory.CreateDBFactory();
            //---------------Assert Precondition----------------
            Assert.AreEqual(0, @class.UniqueConstraints.Count);
            //---------------Execute Test ----------------------
            DBTable table = factory.CreateDBTable(@class);
            //---------------Test Result -----------------------
            Assert.AreEqual(0, table.Keys.Count);
        }

        [Test]
        public void Test_CreateDBTable_WhenUniqueKeyNotNull()
        {
            //---------------Set up test pack-------------------
            DMClass @class = TestUtilsDMClass.CreateUnsavedValidDMClass();
            DMProperty dmProperty = TestUtilsShared.CreateUnsavedValidDMProperty(@class);
            var properties = new List<DMProperty>();
            properties.Add(dmProperty);
            @class.CreateUnsavedUniqueConstraint(properties);
            DBFactory factory = TestDBFactory.CreateDBFactory();
            //---------------Assert Precondition----------------
            Assert.AreEqual(1, @class.UniqueConstraints.Count);
            //---------------Execute Test ----------------------
            DBTable table = factory.CreateDBTable(@class);
            //---------------Test Result -----------------------
            Assert.AreEqual(1, table.Keys.Count);
        }

        [Test]
        public void Test_CreateDBTable_When3UniqueKeys()
        {
            //---------------Set up test pack-------------------
            DMClass @class = TestUtilsDMClass.CreateUnsavedValidDMClass();
            DMProperty dmProperty = TestUtilsShared.CreateUnsavedValidDMProperty(@class);
            var properties = new List<DMProperty>();
            properties.Add(dmProperty);
            @class.CreateUnsavedUniqueConstraint(properties);

            DMProperty dmProperty2 = TestUtilsShared.CreateUnsavedValidDMProperty(@class);
            var properties2 = new List<DMProperty>();
            properties2.Add(dmProperty2);
            @class.CreateUnsavedUniqueConstraint(properties2);

            DMProperty dmProperty3 = TestUtilsShared.CreateUnsavedValidDMProperty(@class);
            var properties3 = new List<DMProperty>();
            properties3.Add(dmProperty3);
            @class.CreateUnsavedUniqueConstraint(properties3);

            DBFactory factory = TestDBFactory.CreateDBFactory();
            //---------------Assert Precondition----------------
            Assert.AreEqual(3, @class.UniqueConstraints.Count);
            //---------------Execute Test ----------------------
            DBTable table = factory.CreateDBTable(@class);
            //---------------Test Result -----------------------
            Assert.AreEqual(3, table.Keys.Count);
        }

        [Test]
        public void Test_CreateDBForeignKeys_OneToOneRelationship()
        {
            //---------------Set up test pack-------------------
            List<DMClass> dmclasses = TestDBFactory.CreateDMClassesWithRelationshipBetween(Cardinality.Single, Cardinality.Single);
            DBFactory factory = TestDBFactory.CreateDBFactory();
            DMClass @class = dmclasses[0];
            DMClass relatedClass = dmclasses[1];
            DBTable table = factory.CreateDBTable(@class);
            DBTable table2 = factory.CreateDBTable(relatedClass);
            List<DBTable> tables = new List<DBTable>() {table, table2};

            //---------------Assert Precondition----------------
            Assert.AreEqual(0, table.ForeignKeys.Count);
            Assert.AreEqual(0, table2.ForeignKeys.Count);
            //---------------Execute Test ----------------------
            factory.CreateDBForeignKeys(tables, dmclasses.Cast<IClassDef>());
            //---------------Test Result -----------------------
            Assert.AreEqual(1, table.ForeignKeys.Count);
            Assert.AreEqual(0, table2.ForeignKeys.Count);

            DBForeignKey foreignKey = table.ForeignKeys[0];
            Assert.AreSame(table, foreignKey.ParentTable);
            Assert.AreSame(table2, foreignKey.RelatedTable);
            Assert.AreEqual(1, foreignKey.ForeignKeyColumns.Count);
            Assert.AreEqual(@class.Properties[0].MappedDBColumnName, foreignKey.ForeignKeyColumns[0].ParentColumnName);
            Assert.AreEqual(relatedClass.Properties[0].MappedDBColumnName, foreignKey.ForeignKeyColumns[0].RelatedColumnName);
        }

        [Test]
        public void Test_CreateDBForeignKeys_OneToManyRelationship()
        {
            //---------------Set up test pack-------------------
            List<DMClass> dmclasses = TestDBFactory.CreateDMClassesWithRelationshipBetween(Cardinality.Single, Cardinality.Multiple);
            DBFactory factory = TestDBFactory.CreateDBFactory();
            DMClass @class = dmclasses[0];
            DMClass relatedClass = dmclasses[1];
            DBTable table = factory.CreateDBTable(@class);
            DBTable table2 = factory.CreateDBTable(relatedClass);
            List<DBTable> tables = new List<DBTable>() { table, table2 };

            //---------------Assert Precondition----------------
            Assert.AreEqual(0, table.ForeignKeys.Count);
            Assert.AreEqual(0, table2.ForeignKeys.Count);
            //---------------Execute Test ----------------------
            factory.CreateDBForeignKeys(tables, dmclasses.Cast<IClassDef>());
            //---------------Test Result -----------------------
            Assert.AreEqual(1, table.ForeignKeys.Count);
            Assert.AreEqual(0, table2.ForeignKeys.Count);

            DBForeignKey foreignKey = table.ForeignKeys[0];
            Assert.AreSame(table, foreignKey.ParentTable);
            Assert.AreSame(table2, foreignKey.RelatedTable);
            Assert.AreEqual(1, foreignKey.ForeignKeyColumns.Count);
            Assert.AreEqual(@class.Properties[0].MappedDBColumnName, foreignKey.ForeignKeyColumns[0].ParentColumnName);
            Assert.AreEqual(relatedClass.Properties[0].MappedDBColumnName, foreignKey.ForeignKeyColumns[0].RelatedColumnName);
        }

        [Test]
        public void Test_CreateDBForeignKeys_ManyToOneRelationship()
        {
            //---------------Set up test pack-------------------
            List<DMClass> dmclasses = TestDBFactory.CreateDMClassesWithRelationshipBetween(Cardinality.Multiple, Cardinality.Single);
            DBFactory factory = TestDBFactory.CreateDBFactory();
            DMClass @class = dmclasses[0];
            DMClass relatedClass = dmclasses[1];
            DBTable table = factory.CreateDBTable(@class);
            DBTable relatedTable = factory.CreateDBTable(relatedClass);
            List<DBTable> tables = new List<DBTable>() { table, relatedTable };

            //---------------Assert Precondition----------------
            Assert.AreEqual(0, table.ForeignKeys.Count);
            Assert.AreEqual(0, relatedTable.ForeignKeys.Count);
            //---------------Execute Test ----------------------
            factory.CreateDBForeignKeys(tables, dmclasses.Cast<IClassDef>());
            //---------------Test Result -----------------------
            Assert.AreEqual(0, table.ForeignKeys.Count);
            Assert.AreEqual(1, relatedTable.ForeignKeys.Count);

            DBForeignKey foreignKey = relatedTable.ForeignKeys[0];
            Assert.AreSame(relatedTable, foreignKey.ParentTable);
            Assert.AreSame(table, foreignKey.RelatedTable);
            Assert.AreEqual(1, foreignKey.ForeignKeyColumns.Count);
            Assert.AreEqual(relatedClass.Properties[0].MappedDBColumnName, foreignKey.ForeignKeyColumns[0].ParentColumnName);
            Assert.AreEqual(@class.Properties[0].MappedDBColumnName, foreignKey.ForeignKeyColumns[0].RelatedColumnName);
        }

    }
}
